home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Toolbox / Fragment Tool / ReadMe- Fragment Tool next >
Encoding:
Text File  |  1995-11-20  |  29.4 KB  |  368 lines  |  [TEXT/ttxt]

  1. Fragment Tool
  2.  
  3.  
  4. Written by:    Chris White,  Developer Technical Support
  5.     
  6. Copyright:    © 1995 by Apple Computer, Inc. All rights reserved.
  7.     
  8.  
  9. What it does
  10.  
  11. "Fragment Tool" is a simple application designed to allow basic manipulation of code fragments. It allows you to combine or separate several code fragments, and view and edit various pieces of information associated with each code fragment. It demonstrates several Toolbox Managers and  several common, and some not so common, features of these Managers:
  12.     • Code Fragment Manager
  13.         -Loading and preparing a code fragment from the data fork of a file.
  14.         -Retrieving a list of exported symbols from a prepared code fragment.
  15.         -Interpreting and manipulating the 'cfrg' resource.
  16.     • Drag Manager
  17.         -Creating an application specific file when content is dragged to the Finder.
  18.         -Using drag data which makes sense only to your own application. 
  19.         -Dragable lists.
  20.     • List Manager
  21.         -Both 68K and PowerPC native click loop procedures  (there's a gotcha with a native click loop procedure).
  22.         -Non standard text styles in lists.
  23.         -Dragable lists. (Okay, I've mentioned that already, but I couldn't decide what heading it under.)
  24.         -Using lists in document windows.
  25.     • Resource Manager
  26.         -Opening resource forks without loading all preloaded resources. This is particularly important when opening
  27.      application resource forks that may contain preloaded 'CODE' resources.
  28.     • Dialog Manager
  29.         -Non standard text styles in a dialog, including popup menus and editable text items.
  30.         -Support a number of moveable modal dialogs in an application.
  31.  
  32.  
  33. It also demonstrates:
  34.     -How to properly support the standard event loop, including basic support for multiple monitors.
  35.     -Use of the new accessor routines which are provided as the first step to being Copland-savvy.
  36.     -How to safely check if a system feature is available in a native PowerPC application (Gestalt isn't always enough).
  37.     -A stream 'class' that allows you to stream data elements into a memory block, and then retrieve the elements later. 
  38.     -An example of using function pointers for your own purposes.
  39.     -How to get access to the Temporary Items Folder and use it to keep temporary files. 
  40.  
  41. This is not intended to be a definitive 'document' on how to implement these features, but illustrates one approach you can take. 
  42.  
  43.  
  44. How to use the Application
  45.  
  46. This application will allow you to open 'documents' that contain a number of code fragments. The documents must have a 'cfrg' resource, but they can be of any type. This allows you to open both shared libraries and native applications, along with any other files that contain a 'cfrg' resource. Once opened, the documents lists the code fragments contained within the files. With this release, there isn't any direct support for code fragments that are stored in resources. However, the documents list of fragments and the Get Info dialog (described below) should work with these types of code fragments. 
  47.  
  48. Once you are presented with a list of code fragments, you can perform a number of operation on them. If the Drag Manager is available, you can drag fragments to other open documents to move them from one file to another. You can also make copies of them by holding down the option key when you drag. If the Drag Manager is not available, you can use the appropriate menu items under the “Fragments” menu. You can also create new “untitled” documents which are saved as shared libraries.  The only method of adding new document content is by moving or copying existing fragments to these documents. This allows you to better organize your shared libraries without modifying your build process, and may be especially useful for those developers who use the Metrowerks and Symantec development environments.
  49.  
  50. An example library is included for you to get the feel of the application. Other good example is the SOM shared library. However, it would be a good idea to make copies of any system software code fragments BEFORE you start experimenting on them.
  51.  
  52.  
  53. Get Info Dialog
  54.  
  55. The “Get Info” option under the “Fragments” menu displays the contents of the 'cfrg' resource. You can edit some of the fields, but not all of them. It just doesn't make sense to change the targeted processor architecture type, for example. The kinds of things you can change are the name of the code fragment, update level, version numbers, and application stack size. For a comprehensive understanding of all the field contained within this dialog, refer to “Inside Macintosh: PowerPC System Software”, Chapter 3 “Code Fragment Manager”, Page 28 “The Code Fragment Resource”.
  56.  
  57. It is possible for a single code fragment to have a number of additional names to which the fragment can be referred to. While the application does not explicitly support this, you can recognize such fragments by comparing the offset and length fields. If two or more fragments have identical offsets and lengths in the same file, you can assume they refer to the same fragment. If you try to copy such fragments using the different names, the application will currently make two or more copies of the same fragment.
  58.  
  59.  
  60. Exports Dialog
  61.  
  62. The “Exports” option under the “Fragments” menu attempts to open a connection to the fragment and list its exported symbols. There may be times when the fragment you attempt to open will have dependencies that cannot be resolved. These will be due to missing fragments or fragments that exist, but cannot be found in the normal search path.  At some point in the near future, I hope to be able to minimize the occurrence of this by temporarily adding the file and folder of the opened documents to the search path. Once this is implemented, if related code fragments are kept within the same folders or files this problem should not be encountered.
  63.  
  64. While everything else will work okay, you will not be able to get a list of exported symbols from a fragment that cannot execute under the current architecture. For example, when used on a Power Macintosh you will not be able to list the exports of a m68K fragment. Likewise, when used on a 68K machine running CFM68K, you will not be able to list the exports of a pwpc fragment. This is currently how the Code Fragment Manager handles the different architecture, and is designed to stop an application from trying to execute code for a different processor.
  65.  
  66.  
  67. Limitations and Bugs
  68.  
  69. Currently, if you copy a fragment more than once to the same document, the application will make two or more copies of the same fragment.
  70.  
  71. If you copy a fragment from a shared library that contains resources in it's resource fork, no effort will be made to copy the resources. This could result in the copied shared library returning errors or crashing during use.
  72.  
  73. All the routines that manipulate the data fork use quite basic algorithms. They assume enough memory will be available to read all the data into a buffer in a single operation. This quite drastically increases the required memory partition.
  74.  
  75.  
  76. Building
  77.  
  78. Fragment Tool has been built under:
  79.     Metrowerks CodeWarrior 7
  80.     Symantec C++ 8.0.1
  81.     Symantec 7.0.4
  82.     MPW E.T.O. #18- 'Latest MPW': MPW C, PPCC, Symantec C++ for MPW and MrC.
  83.  
  84. The Symantec environments are using a slightly older version of the Universal Interfaces than MPW and CodeWarrior. Because the source code uses some of the new accessor routines which are provided as the first step to being Copland-savvy, you will need to use a later version of the Universal Interfaces than is provided with the Symantec products, or make some changes to the source code. To change the Universal Interfaces, simply place brackets around the existing folder and place the folder containing the later version into the same folder as the existing ones. The brackets will prevent the development environment from using the files contained within the old folder.
  85.  
  86. The new accessor routines will allow you to remove direct accesses to some of the toolbox data structures, at a source code level. Currently, these accessors are implemented as macros. However, they will be available as true API entry points in Copland.
  87.  
  88. Two MPW make files are included: to build a 68K and a 'fat' version using 'Latest MPW'. The 'Latest MPW' set up defaults to using the MPW C and PPCC compilers, but you can change the {C} and {PPCC} variables to SC and MrC if you have these compilers installed. You will need to alter the compiler options as appropriate and use the 'Latest MPW' interfaces and libraries rather than SCLibraries and SCIncludes. To build with 'PreRelease MPW' create a make file using 'Create Build Commands' and ensure that 'qd' is defined in all cases, since the PreRelease MPW Libraries do not define 'qd'. 
  89.  
  90. You will need to install 'DragLib' in the 'Latest MPW' 'Libraries:Shared Libraries' folder in order to get the application to build.
  91.  
  92.  
  93. Data Storage
  94.  
  95.  
  96. There are a number of data structures used internally to the application. A record is hung off each window and dialog, as shown below. They are allocated using the NewPtr routine, which is then assigned to the window's refCon field.
  97.  
  98. typedef struct WindowInfo
  99. {
  100.     Boolean        bUntitled;
  101.     Boolean        bDirty;
  102.     ListRef        listRef;
  103.     Handle        dataHandle;
  104.     FSSpec        fileSpec;
  105.     
  106. } tWindowInfo, *tWindowInfoPtr;
  107.  
  108.  
  109. typedef struct DialogInfo
  110. {
  111.     int16        windowType;
  112.     int16        windowSubType;
  113.     ListHandle    listRef;
  114.     int32        refCon;
  115.     
  116. } tDialogInfo, *tDialogInfoPtr;
  117.  
  118.  
  119. The data handle field in the document record is used to store a tHeader record. The tHeader record contains an array of tItem records, one for each fragment contained within the document. 
  120.  
  121. struct internalItem
  122. {
  123.     /* Data used for the application */
  124.     Boolean            bDeleted;
  125.     Boolean            bExistsInDocument;
  126.     tTempFilePtr    tempFilePtr;
  127.     
  128.     /* Data included in the 'cfrg' resource */
  129.     OSType    archType;
  130.     long    updateLevel;
  131.     long    currVersion;
  132.     long    oldDefVersion;
  133.     long    appStackSize;
  134.     short    appSubFolder;
  135.     short    usage;
  136.     short    location;
  137.     long    codeOffset;
  138.     long    codeLength;
  139.     Str255    name;
  140. };
  141. typedef struct internalItem tItem, *tItemPtr;
  142.  
  143.  
  144. struct internalResource
  145. {
  146.     long             version;
  147.     long             itemCount;
  148.     tItem            itemList[1];
  149. };
  150. typedef struct internalResource tHeader, *tHeaderPtr, **tHeaderHan;
  151.  
  152.  
  153. For each temporary file created to store a fragment's data fork content, we use a tTempFileRec record. The tItem record may or may not have a valid a pointer to one of these records. A record is only created when a fragment is copied or moved, and a temporary file created. If the same fragment is copied again, the new fragment becomes another user of the record. A usage count is maintained, along with the FSSpec to the temporary file. As each document is closed, a fragment's relationship to its temporary file is no longer needed and the usage count is decremented. When no more fragments are relying on the temporary file for it's data, the temporary file is deleted.
  154.  
  155. struct TempFileRec
  156. {
  157.     int        usageCount;
  158.     FSSpec    fileSpec;
  159. };
  160. typedef struct TempFileRec tTempFileRec, *tTempFilePtr;
  161.  
  162.  
  163.  
  164. Strategies
  165.  
  166.  
  167. File I/O
  168.  
  169. Because each code fragment could be of a considerable size, it isn't practical to try and keep all the open fragments in memory.  However, if the application were to follow the classic Macintosh application model and allow the user to save documents as and when he/she sees fit, some synchronization problems would need to be overcome. 
  170.  
  171. For our purposes, each fragment is essentially stored across two locations. With the first implementation, the data fork stores the actual code fragment, and the resource fork stores the information that describes the code fragment and its characteristics. Since the information stored in the resource fork (the 'cfrg' resource) is of a manageable size, it can be kept in memory while the document is open. However, this is not the case for the information stored in the data fork. So, what exactly are the synchronization problems? Well, by way of illustration, consider two documents, A and B. Each document contain a single fragment, A and B respectively. Fragment B is copied to document A and the document is not saved. Fragment B is deleted from document B and the document is saved. At this point, fragment B would be deleted from the data fork of document B. Of course, in a normal application document A would contain all the data in memory, and when the user saved the document, it would be written out to the file. However, in this case the data fork portion of fragment B does not exist in memory, and, in this example, it no longer exists on disk. If document A is now saved, it can only contain as much of fragment B as it has in memory, which isn't a great deal.
  172.  
  173.  
  174. There are three main approaches we could take to solve this problem:-
  175.  
  176. • Don't let the user save documents
  177. When an operation is carried out by the user, the application performs all the file I/O immediately, and the documents never need to be saved.
  178.  
  179. Pros and Cons:
  180. This approach should give relatively fast operations, but may be outweighed by the lack of functionality. People make mistakes, and this approach is unforgiving to the user. Undo would be difficult to implement well, and operations that are undone would require additional file I/O. It doesn't follow the classic Macintosh application model very well, which may confuse some users.
  181.  
  182.  
  183. • Temporary file approach
  184. When a code fragment is manipulated in such way as to affect its data fork portion, a temporary file is created. The data fork portion is read from the original document and written into to the temporary file. When the new document is saved, the data fork portion is always available in the temporary file, no matter what operations have since been carried out on the original document. At that time, it can be read from the temporary file and written into the new document.
  185.  
  186. Pros and Cons:
  187. Given the general sizes of code fragments, the overhead incurred with the extra file I/O is not prohibitive. This also applies to the increased use of disk space. This approach should also allow us to implement additional features such as undo. In fact, anything that requires access to the data fork portion of a deleted fragment, once the document has been saved. It also lends itself to caching schemes and other methods of increasing performance and perceived performance. When the same fragment is operated upon more than once, a temporary file need not be created each time. Multiple operations on the same fragment would not involve a file I/O operation until the user saves the document. It would even be possible to reuse temporary files created from a previous launch. Also, once Copland is available with its file mapped memory, it should be easy to allocate the storage from memory if it's available, or file mapped memory if it isn't. The application then becomes very scaleable, and should work well with both large and small fragments. It would also be possible to use threads to create some of the temporary files before the user carried out an operation. This would allow previously unused cycles to be put to good use. On the down side, if nothing else applies as far as reusing temporary files etc. goes, the data fork portion must be copied twice; first to the temporary file, and then to the new document. Disk space usage is also increased.
  188.  
  189.  
  190. • Hybrid approach (for the sake of a label)
  191. Perform only the file I/O that is absolutely necessary to avoid the synchronization problems described above. For example, when a fragment is copied, copy the data fork portion but don't update the resource fork until the document is saved. While the fragment data is always available, this approach presents it's own synchronization problems. The first case is when a fragment is deleted but the document is not saved. Here, the document will contain the resource fork information for a fragment that does not exist in the data fork. The second case is when a fragment is copied and the document is not saved. Here, the data fork will contain a fragment while the resource fork data does not exist for it. However, both of these situation can be overcome by implementing a clean-up operation that is performed every time a document is closed (even without saving). For example, in the first case described above, the delete operation is deferred to the cleanup operation. The cleanup operation will delete the data fork portion of the fragment only when the document is saved. In the second case, the cleanup operation will delete the copied fragment data if the document is closed without being saved.
  192.  
  193. Pros and Cons:
  194. This solution may seem convoluted and prone to carrying out needless operations. First the data fork portion is copied to the new document. Then, if the user doesn't save it, it's promptly deleted. But if you consider the overhead incurred with the basic approach of the alternative, it starts to make some sense. This approach involves a single copy in most cases. Also, there is a good chance the delete operation will simply involve setting the logical EOF. On the down side, it could involve copying large portions of the data fork to delete a fragment that was never actually saved by the user as part of the document. In addition, a file I/O operation must occur with multiple operations on the same fragment. For these reasons, it doesn't scale very well. It becomes very difficult to recover the data fork portion of a fragment once a document has been saved. This makes things like undo difficult to implement well, and some features impossible to implement at all. Finally, the fact that some file I/O may occur when a document is closed without being saved, may cause the user some concern.
  195.  
  196.  
  197. • The approach used
  198. I decided to use the Temporary file approach. This is much cleaner, and simpler, and allows the application to continue to be developed in the future. One other thing worth mentioning regarding the file I/O is when the files are opened and closed. Because some shared libraries use their own resources, we'll only keep them open during an open and a save operation. This will minimize any problems caused by an application launching and not having access to its resources. Many would crash as a result of the Fragment Tool having the resource fork open. The other option would be to keep the resource fork open read-only. However, this is dangerous because it can lead to the resource map and data becoming inconsistent.
  199.  
  200.  
  201. Error Handling
  202.  
  203. There are two guidelines that have been used to try and avoid the normal sort of problems that can occur as the result of an error exception. First, each routine cleans up after itself. Second, each routine leaves everything in the same state as it found it in.
  204.  
  205. Having each routine clean up after itself can lead to a number of difficulties in C as far as readability and maintainability are concerned. A block of code dedicated to cleaning up and returning from a routine can be a few lines in length. Duplicating this after each error code leads to inconsistencies, errors, and reduced readability. It's worth using C++ just for the exception handling features, but if you must use C you may want to use a similar approach to this. On occasion, we've used a goto statement to jump to a block of code at the end of the routine. If the clean up code should only be called as the result of an exception, the routine returns before dropping into this clean up code.
  206.  
  207. If the current port or resource file is changed, restore them before leaving the routine. Pay particular attention to this when an error occurs because this is usually the place where it's likely to be overlooked. If a handle needs to be in a particular state, save its current state using the HGetState routine before changing it. The HSetState routine can then be used to restore it, and the problems associated with a routine unintentional changing the state of a handle can be avoided.
  208.  
  209. Runtime errors that should never occur in a bug free program can use a DebugStr call. These can be conditionally compile using a #define. For example, our GetNthItem routine returns a pointer to a fragment's data structure, tItem. The routine should never return a nil pointer, so we use the following code snippet to prevent the application from crashing, and we can also get an idea of what may have gone wrong. 
  210.  
  211. #if DEBUGGING
  212. if ( theItem == nil )    DebugStr ( "\p GetNthItem returning nil" );
  213. #endif
  214.  
  215.  
  216. Locking Memory
  217.  
  218. The CopyFragment routine is a good example of a routine that doesn't _require_ memory to be locked given the current project. The way the program is now, nothing can move memory. Even if it could, there may be nothing in the routine that required memory to be locked down. However, there are a number of situations that can occur which may allow memory to move, and similar situations that could require memory to be locked down. For example, the LoadSeg routine is called implicitly whenever a function is called which is in an unloaded segment. Since LoadSeg can move memory, we need to be careful of this occurring. This could happen after re-segmentation or a source code change that makes a call to a routine in another segment. Also, optimizing compilers can dereference handles and store the pointer to be used later in the code. This situation requires memory to be locked, but isn't usually reflected in the source code. So, unless you're the only engineer working on the source code and you're prepared to re-evaluate the situation with every change you make, you should lock down those handles.
  219.  
  220.  
  221.  
  222. How the Interesting Stuff Works
  223.  
  224.  
  225. Copying and Moving Fragments
  226.  
  227. This is quite a simple operation that involves manipulating the 'cfrg' resource and the data fork of the file. The 'cfrg' resource contains a header record, followed by an entry for each of the code fragments contained within the file. This entry contains all the information presented in the Get Info dialog, which includes an offset and a length to the fragment data in the data fork of the file. Armed with this information, it's a relatively simple process to add the record to the target file's 'cfrg' resource and append the fragment data to the target file's data fork. The offset field is then updated to reflect the new offset, and that's about it.
  228.  
  229.  
  230. Get Info
  231.  
  232. This simply displays the information contained within the 'cfrg' resource. If the user changes any of the information, the resource is updated.
  233.  
  234.  
  235. Exports
  236.  
  237. This uses the Code Fragment Manager API  to open a connection to the code fragment, and get the information about each exported symbol it contains.  To open the connection to a fragment located in the data fork of a file, you use the following call:
  238.  
  239. theErr = GetDiskFragment ( theRec->u.out.theSpecPtr, theItem->codeOffset, theItem->codeLength,
  240.                                                                                                             theItem->name, theFlags, &theConnID, &mainAddr, errName );
  241.  
  242.  
  243. Now, with the connection ID returned from the GetDiskFragment call, we can get a counter of the number of symbols the fragment exports. This is done with the following call:
  244.  
  245. theErr = CountSymbols ( theConnID, &theCount );
  246.  
  247.  
  248. and finally, we iterate through  the symbols and get the information we need. This is done using the following call:
  249.  
  250. theErr = GetIndSymbol ( theConnID, i, symName, &symAddr, &symClass );
  251.  
  252.  
  253. It's then just a case of displaying the information in the List Manager list. The complete source code can be found in the AddFragmentExports routine inside DialogStuff.c
  254.  
  255.  
  256. Native List Manager ClickLoop Procedure
  257.  
  258. There's a gotcha with a native implementation of the List Manager's ClickLoop procedure. This application demonstrates a rather nasty, but effective way to deal with the problem.
  259.  
  260. The Problem:
  261. While ClickLoops were originally specified as pascal functions, if you check Inside Macintosh you'll see that the application-defined routine should set register D0 to 1 or 0 to indicate to the List Manager if the routine should be called again. Since this is how a C function returns its result, the uppListClickLoopProcInfo constant was defined as a C-based routine which returned a Boolean value. Unfortunately, it turns out the List Manager doesn't actually check D0, but rather simply tests the Z-bit in the 68K processor's status register (SR). As you may know, the Z-bit (or Zero flag) is set if the last data value processed was zero. While the Mixed Mode Manager does an excellent job of moving the value returned by the native code into the emulator's D0 register, it doesn't extend to setting the emulator's status register (SR) Z-bit. As this behaviour is lost during a Mixed Mode switch, the test which the List Manager performs is unreliable.
  262.  
  263. A Solution:
  264. The work-around used here is to write a 68K stub routine that calls the native PowerPC code and then tests D0 before returning to the List Manager. The emulator will then set its Z-bit, and the List Manager will be happy. A fairly nasty way to do this from native PowerPC code is to build the 68K code into a data structure and pass it as the ListClickLoop routine. The code snippet below demonstrates the approach, and is taken from the InitListClickLoop routine found in Lists.c
  265.  
  266. #ifdef powerc
  267.  
  268.     static RoutineDescriptor theListClickLoopRD =
  269.                 BUILD_ROUTINE_DESCRIPTOR ( uppListClickLoopProcInfo, MyClickLoop );
  270.     
  271.     
  272.     #ifdef powerc
  273.     #pragma options align=mac68k
  274.     #endif
  275.     
  276.     static struct LClickLoopGlue
  277.     {
  278.        long     move;                                                                        // MOVEA.L  ClickLoopUPP, A0
  279.        short    jsr;                                                                            // JSR  (A0)
  280.        short    tst;                                                                            // TST.B D0
  281.        short    rts;                                                                            // RTS
  282.        UniversalProcPtr ClickLoopUPP;        // Storage for the UPP
  283.     } LClickLoop68K = {
  284.        0x207A0008,
  285.        0x4E90,
  286.        0x4A00,
  287.        0x4E75,
  288.        (UniversalProcPtr) &theListClickLoopRD
  289.     };
  290.     
  291.     #ifdef powerc
  292.     #pragma options align=reset
  293.     #endif
  294.     
  295.     gClickLoopUPP = (ListClickLoopUPP) &LClickLoop68K;
  296.     
  297. #else
  298.     gClickLoopUPP = NewListClickLoopProc ( MyClickLoop );
  299. #endif
  300.  
  301. You can see from the 68K version of the code that gClickLoopUPP is the Universal Procedure Pointer you give to the List Manager, and MyClickLoop is the name of the application-defined routine.
  302.  
  303.  
  304. Drag Manager Support
  305.  
  306.  
  307. Most of the code that deals with the Drag Manager is in DragStuff.c.  Although Gestalt is used to check if the Drag Manager is available, a number of situations can occur that may cause the DragLib shared library to fail to load. Because we're weak linking to the library, we need to check we really have a connection to the shared library. This can be done quite simply by checking any symbol against the constant kUnresolvedSymbolAddress. The following code snippet shown this, and is done after Gestalt has been used in the CheckConfiguration routine.
  308.  
  309. #if GENERATINGCFM
  310.     if ( gHasDragManager )
  311.         gHasDragManager = (InstallTrackingHandler != (void*) kUnresolvedSymbolAddress);
  312. #endif
  313.  
  314.  
  315. So what can cause a shared library to fail to link? Well, the most common cause is a low memory situation. Even if the code is already loaded, a shared library may still allocate a new copy of its global data section.
  316.  
  317. One other thing of interest is the code that creates a file when the user drags some content to the Finder. This is done by promising a HFS flavor to the Drag Manager. The AddHFSPromise routine performs this task, and look something like this:
  318.  
  319. OSErr AddHFSPromise ( DragReference theDrag, ItemReference theItem )
  320. {
  321.     OSErr                    theErr;
  322.     PromiseHFSFlavor    thePromise;
  323.     
  324.     
  325.     thePromise.fileType = kCFragLibraryFileType;
  326.     thePromise.fileCreator = kFourQuestionMarks;
  327.     thePromise.fdFlags = 0;
  328.     thePromise.promisedFlavor = kCFragLibraryFileType;
  329.     
  330.     theErr = AddDragItemFlavor ( theDrag, theItem, flavorTypePromiseHFS, &thePromise,
  331.                                         sizeof ( PromiseHFSFlavor ), 0L );
  332.     if ( theErr == noErr )
  333.         // Here's the promised flavor we're going to deliver
  334.         theErr = AddDragItemFlavor ( theDrag, theItem, kCFragLibraryFileType, nil, 0L, 0L );
  335.  
  336.     
  337.     return theErr;
  338. }
  339.  
  340. We use the PromiseHFSFlavor data structure to tell the Drag Manager what file we'll create, and then add the flavor using the flavorTypePromiseHFS constant. The promisedFlavor can be anything we like, just as long as we add a drag item that has the same type, and we then set it in our send data proc. If we don't do this, the Drag Manager will abort the operation which will cause the zoomback.  In the send data proc, we create the file in the Temporary Items folder and add the content. Finally, we call the SetDragItemFlavorData routine to tell the Drag Manager where the file is. There's no need to locate the target of the drop and create the file in that location. The system will move the file to the correct location for use. So we simply use the following snippet:
  341.  
  342.         result = SetDragItemFlavorData ( theDragRef, theItemRef, kCFragLibraryFileType,
  343.                                                                                                                                                                 &locationSpec, sizeof ( FSSpec ), 0L );
  344.  
  345.  
  346. One other thing, there have been times when a bug in the Drag Manager caused this type of operation to fail. If you experience problems with your own implementation of this, try making the PromiseHFSFlavor the very first flavor you add.
  347.  
  348.  
  349.  
  350. Further things YOU could do
  351.  
  352.  
  353. -Support resource based fragments.
  354. -Allow additional names to be created for a single fragment. This could be done by dropping a fragment in a file where it already exists. The lists could be improved to show these 'aliases' in an italic style. This would allow a fragment to be loaded using a number of different names, and will better distinguish those fragments that already use the technique. For an example, open the SOM shared library.
  355. -Allow a folder to be chosen in the “Get Info” dialog,  add an 'alis' resource to the file, and store its ID in the folder ID
  356.     field within the 'cfrg' resource.
  357. -Allow the lists be sorted depending on a click on the column heading, similar to the Finder. Improve list navigation by
  358.     supporting first character selection (depending on current column), and cursor keys.
  359. -Include scrap support.
  360. -Include further support for the Drag Manager. Add Drag support to other windows, and add support for 'TEXT' data
  361.     flavors.
  362.  
  363.  
  364.  
  365. Chris White - Sept. '95
  366. © 1995 by Apple Computer, Inc. All rights reserved.
  367.  
  368.